#include "pch.h"
#include "ESurface.h"
#include "DirectXExt.h"
#include <iostream>
#include <time.h>
using namespace CnyeCPlus::ExtSurface;
using namespace Platform;
using namespace Microsoft::WRL;

ESurfaceImageSource::ESurfaceImageSource(int pixelWidth, int pixelHeight, bool isOpaque) :
	SurfaceImageSource(pixelWidth, pixelHeight, isOpaque)
{
	m_width = pixelWidth;
	m_height = pixelHeight;
	this->StrokeWidth=1.0f;
	this->m_FType=ref new Platform::Collections::Map<int, FreeType^>;
	this->tval=0;
	CreateDeviceIndependentResources();
	CreateDeviceResources();
}

// Initialize resources that are independent of hardware.
void ESurfaceImageSource::CreateDeviceIndependentResources()
{
	// Query for ISurfaceImageSourceNative interface.
	DX::ThrowIfFailed(
		reinterpret_cast<IUnknown*>(this)->QueryInterface(IID_PPV_ARGS(&m_sisNative))
		);
}

// Initialize hardware-dependent resources.
void ESurfaceImageSource::CreateDeviceResources()
{
	// This flag adds support for surfaces with a different color channel ordering
	// than the API default. It is required for compatibility with Direct2D.
	UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; 

#if defined(_DEBUG)    
	// If the project is in a debug build, enable debugging via SDK Layers.
	creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	// This array defines the set of DirectX hardware feature levels this app will support.
	// Note the ordering should be preserved.
	// Don't forget to declare your application's minimum required feature level in its
	// description.  All applications are assumed to support 9.1 unless otherwise stated.
	const D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
		D3D_FEATURE_LEVEL_9_3,
		D3D_FEATURE_LEVEL_9_2,
		D3D_FEATURE_LEVEL_9_1,
	};

	// Create the Direct3D 11 API device object.
	DX::ThrowIfFailed(
		D3D11CreateDevice(
		nullptr,                        // Specify nullptr to use the default adapter.
		D3D_DRIVER_TYPE_HARDWARE,
		nullptr,
		creationFlags,                  // Set debug and Direct2D compatibility flags.
		featureLevels,                  // List of feature levels this app can support.
		ARRAYSIZE(featureLevels),
		D3D11_SDK_VERSION,              // Always set this to D3D11_SDK_VERSION for Metro style apps.
		&m_d3dDevice,                   // Returns the Direct3D device created.
		nullptr,
		nullptr
		)
		);
	// Get the Direct3D 11.1 API device.
	ComPtr<IDXGIDevice> dxgiDevice;
	DX::ThrowIfFailed(
		m_d3dDevice.As(&dxgiDevice)
		);

	// Create the Direct2D device object and a corresponding context.
	DX::ThrowIfFailed(
		D2D1CreateDevice(
		dxgiDevice.Get(),
		nullptr,
		&m_d2dDevice
		)
		);

	DX::ThrowIfFailed(
		m_d2dDevice->CreateDeviceContext(
		D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
		&m_d2dContext
		)
		);

	// Set DPI to the display's current DPI.
	SetDpi(Windows::Graphics::Display::DisplayProperties::LogicalDpi);

	// Associate the DXGI device with the SurfaceImageSource.
	DX::ThrowIfFailed(
		m_sisNative->SetDevice(dxgiDevice.Get())
		);

	// Create Direct2D factory.
	ID2D1Factory **mfactory=&m_d2dFactory;
	DX::ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,mfactory));
	// Create a shared DirectWrite factory.
	IDWriteFactory **wfactory=&m_dwriteFactory;
	DX::ThrowIfFailed(DWriteCreateFactory(
		DWRITE_FACTORY_TYPE_SHARED,
		__uuidof(IDWriteFactory),
		reinterpret_cast<IUnknown**>(wfactory)
		));

	DX::ThrowIfFailed(CoCreateInstance(
		CLSID_WICImagingFactory,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IWICImagingFactory,
		(LPVOID*)&m_wicFactory
		));
}

// Sets the current DPI.
void ESurfaceImageSource::SetDpi(float dpi)
{
	// Update Direct2D's stored DPI.
	m_d2dContext->SetDpi(dpi, dpi);
}

// Begins drawing, allowing updates to content in the specified area.
void ESurfaceImageSource::BeginDraw(Windows::Foundation::Rect updateRect)
{    
	POINT offset;
	ComPtr<IDXGISurface> surface;

	// Express target area as a native RECT type.
	RECT updateRectNative; 
	updateRectNative.left = static_cast<LONG>(updateRect.Left);
	updateRectNative.top = static_cast<LONG>(updateRect.Top);
	updateRectNative.right = static_cast<LONG>(updateRect.Right);
	updateRectNative.bottom = static_cast<LONG>(updateRect.Bottom);

	// Begin drawing - returns a target surface and an offset to use as the top left origin when drawing.
	HRESULT beginDrawHR = m_sisNative->BeginDraw(updateRectNative, &surface, &offset);

	if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || beginDrawHR == DXGI_ERROR_DEVICE_RESET)
	{
		// If the device has been removed or reset, attempt to recreate it and continue drawing.
		CreateDeviceResources();
		BeginDraw(updateRect);
	}
	else
	{
		// Notify the caller by throwing an exception if any other error was encountered.
		DX::ThrowIfFailed(beginDrawHR);
	}

	// Create render target.
	ComPtr<ID2D1Bitmap1> bitmap;
	DX::ThrowIfFailed(
		m_d2dContext->CreateBitmapFromDxgiSurface(
		surface.Get(),
		nullptr,
		&bitmap
		)
		);

	// Set context's render target.
	m_d2dContext->SetTarget(bitmap.Get());

	// Begin drawing using D2D context.
	m_d2dContext->BeginDraw();

	// Apply a clip and transform to constrain updates to the target update area.
	// This is required to ensure coordinates within the target surface remain
	// consistent by taking into account the offset returned by BeginDraw, and
	// can also improve performance by optimizing the area that is drawn by D2D.
	// Apps should always account for the offset output parameter returned by 
	// BeginDraw, since it may not match the passed updateRect input parameter's location.
	m_d2dContext->PushAxisAlignedClip(
		D2D1::RectF(
		static_cast<float>(offset.x),  
		static_cast<float>(offset.y),  
		static_cast<float>(offset.x + updateRect.Width),
		static_cast<float>(offset.y + updateRect.Height)  
		),  
		D2D1_ANTIALIAS_MODE_ALIASED  
		);

	m_d2dContext->SetTransform(
		D2D1::Matrix3x2F::Translation(
		static_cast<float>(offset.x),
		static_cast<float>(offset.y)
		)
		);
	//this->m_wicFactory.

}

// Ends drawing updates started by a previous BeginDraw call.
void ESurfaceImageSource::EndDraw()
{
	//
	this->FreeStrokeStyle();
	this->FreeTextFormat();
	// Remove the transform and clip applied in BeginDraw since
	// the target area can change on every update.
	m_d2dContext->SetTransform(D2D1::IdentityMatrix());
	m_d2dContext->PopAxisAlignedClip();

	// Remove the render target and end drawing.
	DX::ThrowIfFailed(
		m_d2dContext->EndDraw()
		);

	m_d2dContext->SetTarget(nullptr);

	DX::ThrowIfFailed(
		m_sisNative->EndDraw()
		);
}

// Clears the background with the given color.
void ESurfaceImageSource::Clear(Windows::UI::Color color)
{
	m_d2dContext->Clear(DX::ConvertToColorF(color));
}

// Draws a filled rectangle with the given color and position.
void ESurfaceImageSource::DrawRect(Windows::UI::Color color, Windows::Foundation::Rect rect,bool full)
{
	// Create a solid color D2D brush.
	ID2D1SolidColorBrush* brush;
	DX::ThrowIfFailed(
		m_d2dContext->CreateSolidColorBrush(
		DX::ConvertToColorF(color),
		&brush));
	// Draw a rectangle.
	if(full){
		m_d2dContext->FillRectangle(DX::ConvertToRectF(rect), brush);
	}else{
		m_d2dContext->DrawRectangle(DX::ConvertToRectF(rect), brush,this->StrokeWidth,this->StrokeStyle);
	}
	DX::SafeRelease(&brush);
}
///
void ESurfaceImageSource::CreateStrokeStyle(const Platform::Array<float>^ dashes){
	this->FreeStrokeStyle();
	m_d2dFactory->CreateStrokeStyle(
		D2D1::StrokeStyleProperties(
		D2D1_CAP_STYLE_FLAT,
		D2D1_CAP_STYLE_FLAT,
		D2D1_CAP_STYLE_ROUND,
		D2D1_LINE_JOIN_MITER,
		10.0f,
		D2D1_DASH_STYLE_CUSTOM,
		0.0f),
		dashes->Data,
		dashes->Length,
		&StrokeStyle
		);
}
void ESurfaceImageSource::FreeStrokeStyle(){
	DX::SafeRelease(&StrokeStyle);
}
//
void ESurfaceImageSource::CreateTextFormat(
	Platform::String^ fontFamilyName,float fontSize,int fontWeight,int fontStyle,int fontStretch, Platform::String^ localName){
		this->FreeTextFormat();
		const WCHAR* lname;
		if(nullptr==localName){
			WCHAR _lname[LOCALE_NAME_MAX_LENGTH];
			int lnlen=GetSystemDefaultLocaleName(_lname,LOCALE_NAME_MAX_LENGTH);
			lname=_lname;
		}else{
			lname=localName->Data();
		}
		DX::ThrowIfFailed(m_dwriteFactory->CreateTextFormat(
			fontFamilyName->Data(),                // Font family name.
			NULL,                       // Font collection (NULL sets it to use the system font collection).
			DWRITE_FONT_WEIGHT(fontWeight),
			DWRITE_FONT_STYLE(fontStyle),
			DWRITE_FONT_STRETCH(fontStretch),
			fontSize,
			lname,
			&m_textFormat));
		m_textFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM,fontSize,fontSize*0.8f);
}
void ESurfaceImageSource::FreeTextFormat(){
	DX::SafeRelease(&m_textFormat);
}
void ESurfaceImageSource::SetLineSpacing(LineSpacingMethod lineSpacingMethod,FLOAT lineSpacing,FLOAT baseline){
	switch (lineSpacingMethod)
	{
	case CnyeCPlus::ExtSurface::LineSpacingMethod::DEFAULT:
		m_textFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT,lineSpacing,baseline);
		break;
	case CnyeCPlus::ExtSurface::LineSpacingMethod::UNIFORM:
		m_textFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM,lineSpacing,baseline);
		break;
	default:
		break;
	}
}
void ESurfaceImageSource::DrawText(Platform::String^ text,Windows::UI::Color color,Windows::Foundation::Rect rect){

	// Center align (horizontally) the text.
	//DX::ThrowIfFailed(pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING));
	//DX::ThrowIfFailed(pTextFormat_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR));
	//m_textFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM,30,20);
	// Create a solid color D2D brush.
	ID2D1SolidColorBrush* brush;
	DX::ThrowIfFailed(
		m_d2dContext->CreateSolidColorBrush(
		DX::ConvertToColorF(color),
		&brush));
	D2D1_RECT_F layoutRect =DX::ConvertToRectF(rect);
	const WCHAR* _text=text->Data();
	m_d2dContext->DrawText(
		_text,        // The string to render.
		(UINT32) wcslen(_text),    // The string's length.
		m_textFormat,    // The text format.
		layoutRect,       // The region of the window where the text will be rendered.
		brush     // The brush used to draw the text.
		);
	DX::SafeRelease(&brush);
	//m_d2dContext->CreateBitmapFromWicBitmap(
	//m_d2dContext->c
	//this->m_wicFactory->CreateBitmapFromMemory(
}

void ESurfaceImageSource::CreateFBuilder(const Platform::Array<unsigned char>^ fontbuf,int fid){
	//delete this->_FBuilder;
	if(fid>0){
		if(this->m_FType->HasKey(fid)){
			this->_FBuilder=this->m_FType->Lookup(fid);
		}else{
			this->_FBuilder=ref new FreeType(fontbuf);
			this->m_FType->Insert(fid,this->_FBuilder);
		}
	}else{
		this->_FBuilder=ref new FreeType(fontbuf);
	}
}
void ESurfaceImageSource::SetFFontSize(float fontsize){
	this->FBuilder->SetCharSize(fontsize);
}
Windows::Foundation::Size ESurfaceImageSource::FCharGlyphSize(wchar_t c){

	Windows::Foundation::Size size=this->FBuilder->GenerateGlyph(c);

	return size;
}
void ESurfaceImageSource::DrawFText(
	const Platform::Array<CharRect>^ rts,
	Windows::UI::Color tcolor,Windows::UI::Color bcolor){
		for(uint8 i=0;i<rts->Length;i++){
			CharRect& crt=rts->get(i);
			this->DrawFText(crt.Tchar,crt.Rt,tcolor,bcolor);
		}
}
void ESurfaceImageSource::DrawFText(wchar_t c,Windows::Foundation::Rect rect,Windows::UI::Color tcolor,Windows::UI::Color bcolor){
	//util::Timer::SharedTimer->Beg();
	ID2D1Bitmap1 *bitimg=this->FBuilder->CreateBitmap(this->m_d2dContext.Get(),this->m_wicFactory.Get(),c,tcolor,bcolor);
	//util::Timer::SharedTimer->Delay();
	//
	D2D1_RECT_F layoutRect =DX::ConvertToRectF(rect);
	m_d2dContext->DrawBitmap(bitimg,layoutRect);
	//DX::SafeRelease(&bitimg);
}
void showChar2();
void showChar();
void ESurfaceImageSource::ShowChar(){
	//showChar();
	showChar2();
	int v1=10;
	v1=v1<<6;
	float v2=10.0f;
	v2=v2*2*2*2*2*2*2;
}